﻿using Cofe.Core.Filter;
using Cofe.Core.Property;
using Cofe.Core.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using Cofe.Core.Interfaces;
using Cofe.Core.Logger;

namespace Cofe.Core.Entry
{
    public class AutoEntryListPropertyProvider : EntryListPropertyProvider, IAutoEntryListPropertyProvider
    {

        #region Constructor

        void initFilters(IEntryFilter[] entryFilters, IOptionFilter[] optionFilters)
        {
            //_entryFilters = entryFilters;
            //_optionFilters = optionFilters;
            _filterStr = CofeServices.EntryFilterParser.FilterToFilterString(entryFilters.Cast<IFilter>().Union(optionFilters));

            //updateOutput();

            registerProperties(CofeEntryListProperties.FilterString, () => _filterStr);
            //registerProperties(CofeProperties.FullParseName, () => _filterStr);
            registerProperties(CofeProperties.DefaultInterfaceType, () => typeof(IAutoEntryList));
            registerProperties(CofeProperties.CofeAttributes, () => getBaseCofeAttributes() | CofeAttributes.ChildNotCachable);
            registerProperties(CofeProperties.Type, () => "AutoEntryList");

            //registerAction<IEnumerable<ICofeSystemInfo>>(CofeDirectoryProperties.List,
            //    (pm) => list(pm));
        }

        public AutoEntryListPropertyProvider(string filterStr)
            : base(EntryListTypes.AutoEntryList, filterStr)
        {
            IEntryFilter[] entryFilters; IOptionFilter[] optionFilters;
            CofeServices.EntryFilterParser.ParseFilter(filterStr, out entryFilters, out optionFilters);
            initFilters(entryFilters, optionFilters);
        }

        public AutoEntryListPropertyProvider(IEntryFilter[] entryFilters, IOptionFilter[] optionFilters)
            : base(EntryListTypes.AutoEntryList, CofeServices.EntryFilterParser.FilterToFilterString(
            entryFilters.Cast<IFilter>().Union(optionFilters)))
        {
            initFilters(entryFilters, optionFilters);
        }


        #endregion

        #region Methods

        //private void enableSearchAction()
        //{
        //    if (!(SupportedDefinitions.IsPropertySupported(CofeEntryListProperties.SearchAsync)))
        //    {
        //        registerActionAsync(CofeEntryListProperties.SearchAsync,
        //            (pm) => searchAsync(pm));
        //    }
        //}

        //private void updateOutput()
        //{
        //    _rootDirectories =
        //            (_entryFilters.Any(ef => ef is RootEntryFilter) ?
        //            _entryFilters.Where(ef => ef is RootEntryFilter && !ef.Negative)
        //            .Select(ef => (ef as RootEntryFilter).DirectoryPropertyHost)
        //            .Select(rph => CofeServices.EntryConstructor.ConstructEntry<ICofeDirectoryInfo>(rph)) :
        //            CofeServices.VolumeRegistrar.Behaviors.GetRootDirectories(EntryConstructionFlags.Default)).ToArray();

        //    enableSearchAction();
        //}

        //private async Task searchAsync(ParameterDic pd)
        //{
        //    int count = 0;
        //    List<ICofeSystemInfo> retVal = new List<ICofeSystemInfo>();

        //    foreach (var r in _rootDirectories)
        //    {
        //        ParameterDic tempPd = new ParameterDic { { "EntryFilters", _entryFilters }, { "OptionFilters", _optionFilters }, { "EnableCache", true } };
        //        retVal.AddRange(
        //            await r.Properties.Behaviors.InvokeAsync<IEnumerable<ICofeSystemInfo>>(CofeDirectoryProperties.List, tempPd)
        //            );

        //        if (tempPd.ContainsKey(ParameterKeys.TotalEntries) && tempPd[ParameterKeys.TotalEntries] is int)
        //            count += (int)tempPd[ParameterKeys.TotalEntries];
        //    }

        //    var paginate = _optionFilters.FirstOrDefault(f => f is PaginateOptionFilter) as PaginateOptionFilter;
        //    if (paginate != null && count != 0)
        //    {
        //        pd.AddOrUpdate(ParameterKeys.CurrentPage, paginate.Page);
        //        pd.AddOrUpdate(ParameterKeys.TotalPages, (int)Math.Ceiling((float)count / paginate.ItemsPerPage));
        //        pd.AddOrUpdate(ParameterKeys.TotalEntries, count);
        //        pd.AddOrUpdate(ParameterKeys.ItemsPerPage, paginate.ItemsPerPage);
        //    }

        //    _entryList = new ThreadSafeCachedEnumerable<ICofeSystemInfo>(retVal.Distinct());

        //    //var expression = _rootDirectories.SelectMany(rd => rd.EnumerateCofeSystemInfos(_entryFilters, _optionFilters,
        //    //        EntryConstructionFlags.Default, CofeServices.EntryConstructor))
        //    //        .Select(e => e.Properties)
        //    //        .Distinct();

        //    //expression = FilterUtils.UpdateOptionFilterExpressions(expression, _optionFilters);            
        //    //_entryList = new ThreadSafeCachedEnumerable<IPropertyHost>(expression);
        //}


        //protected IEnumerable<ICofeSystemInfo> list(ParameterDic pd)
        //{
        //    pd.IsHandled = true;
        //    return _entryList;
        //    //return from ph in _entryList select CofeServices.EntryConstructor.ConstructEntry(ph);
        //}



        #endregion

        #region Data

        private string _filterStr;
        //private IObservable<IPropertyProvider> _entryObservable = null;
        //private ICofeDirectoryInfo[] _rootDirectories;
        //private IEntryFilter[] _entryFilters;
        //private IOptionFilter[] _optionFilters;
        //private IEnumerable<ICofeSystemInfo> _entryList = new List<ICofeSystemInfo>();

        private List<object> _supportedProperties = new List<object>();
        private IEntryOptionFilterParser _entryFilterParser = null;

        #endregion

        #region Public Properties

        public IEntryOptionFilterParser EntryFilterParser
        {
            get { return _entryFilterParser != null ? _entryFilterParser : CofeServices.ServiceLocater.FindService<IEntryOptionFilterParser>(); }
            set { _entryFilterParser = value; }
        }

        //public IPropertyDefinitions[] SupportedDefinitions
        //{
        //    get
        //    {
        //        return (from sd in _supportedDefinitions
        //                select sd.Subset(_supportedProperties)).ToArray();
        //    }
        //}

        #endregion



    }

    public class CustomEntryListPrimaryPP : EntryListPropertyProvider
    {
        #region Constructor

        public CustomEntryListPrimaryPP(string label, bool isRoot)
            : base(EntryListTypes.CustomEntryList, label)
        {
            if (isRoot)
                registerProperties(CofeProperties.FullParseName, () => "{" + label + "}");

            registerProperties(CofeProperties.DefaultInterfaceType, () => typeof(ICustomEntryList));
            registerProperties(CofeProperties.Type, () => EntryTypes.CustomEntryList);
            registerActionAsync(CofeDirectoryProperties.CreateAsync, (pm) => createAsync(pm));
            registerProperties(CofeProperties.CofeAttributes, () => getBaseCofeAttributes() | CofeAttributes.SubEntriesCanChange
                | CofeAttributes.ChildNotCachable);

            registerAction<IEnumerable<IPropertyProvider>>(CofeDirectoryProperties.ListCore,
              (pm) => listCore(pm)); //GetLinks then return
        }

        #endregion

        #region Methods

        private Task<IPropertyProvider> createAsync(ParameterDic pm)
        {
            return Task<IPropertyProvider>.Run(() =>
            {
                string type = pm.ContainsKey("Type") ? (string)pm["Type"] : null;
                string name = pm.ContainsKey("Name") ? (string)pm["Name"] : null;

                if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(name))
                    return null;
                //string destName = PathFE.Combine(Entity.FullName, name);

                switch (type.ToLower())
                {
                    case "folder":
                    case "directory":
                    case "customentrylist":
                        try
                        {
                            pm.IsHandled = true;
                            foreach (var pp in this.listCore(new ParameterDic() { PropertyHost = pm.PropertyHost }))
                                if (pp.GetOneProperty<string>(CofeProperties.Label).Equals(name))
                                    return pp;

                            var newEl = CofeServices.EntryListRegistrar.CreateCustomEntryList(name);
                            var newElLink = pm.PropertyHost.Behaviors.Invoke<IPropertyHost>(CofeEntryLinkProperties.AddLink,
                                new ParameterDic() { { "ParseName", newEl.ParseName }, { "Label", name } });
                            return newElLink.PrimaryPropertyProviderChecked;
                        }
                        catch (Exception ex)
                        {
                            pm.Error = ex;
                            return null;
                        }
                    default:
                        return null;
                }
            });
        }


        protected IEnumerable<IPropertyProvider> listCore(ParameterDic pm)
        {
            if (pm.PropertyHost == null)
                return null;

            pm.IsHandled = true;

            var links = pm.PropertyHost.Behaviors.Invoke<IEnumerable<IPropertyHost>>(CofeEntryLinkProperties.GetLinks)
                .Select(l => l.PrimaryPropertyProviderChecked).ToArray();
            return links;
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion
    }

    [Obsolete]
    public class CustomEntryListPropertyProvider : EntryListPropertyProvider, ICustomEntryListPropertyProvider
    {
        #region Constructor

        public CustomEntryListPropertyProvider(string name)
            : base(EntryListTypes.CustomEntryList, name)
        {
            registerProperties(CofeProperties.DefaultInterfaceType, () => typeof(ICustomEntryList));
            registerProperties(CofeProperties.Type, () => "CustomEntryList");
            registerActionAsync(CofeDirectoryProperties.CreateAsync, (pm) => createAsync(pm));
            registerProperties(CofeProperties.CofeAttributes, () => getBaseCofeAttributes() | CofeAttributes.SubEntriesCanChange);

            registerAction<IEnumerable<IPropertyProvider>>(CofeDirectoryProperties.ListCore,
              (pm) => listCore(pm));
        }


        #endregion

        #region Methods

        private Task<IPropertyProvider> createAsync(ParameterDic dic)
        {

            return Task<IPropertyProvider>.Run(() =>
               {
                   string type = dic.ContainsKey("Type") ? (string)dic["Type"] : null;
                   string name = dic.ContainsKey("Name") ? (string)dic["Name"] : null;

                   if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(name))
                       return null;
                   //string destName = PathFE.Combine(Entity.FullName, name);

                   switch (type.ToLower())
                   {
                       case "folder":
                       case "directory":
                       case "customentrylist":
                           try
                           {
                               dic.IsHandled = true;
                               Console.WriteLine(_entryDic);
                               if (_entryDic.ContainsKey(name))
                                   return new CustomEntryLinkPropertyProvider(this, name, _entryDic[name]);

                               var thisEntry = EntryConstructor.ConstructEntry(EntryConstructor.ConstructPropertyHost(this));
                               var newEl = CofeServices.EntryListRegistrar.CreateCustomEntryList(name);
                               //newEl.Properties.Behaviors.SetProperty(CofeProperties.Parent, thisEntry);
                               //var newElLink = new CustomEntryLinkPropertyProvider(this, name, newEl.Properties);
                               var newLink = add(name, null, newEl.Properties);

                               return (IPropertyProvider)newLink;
                           }
                           catch (Exception ex)
                           {
                               dic.Error = ex;
                               return null;
                           }
                       default:
                           return null;
                   }
               });
        }


        public IPropertyProvider add(string name, string entryParsePath, IPropertyHost entryPH)
        {
            CustomEntryLinkPropertyProvider entryLinkPP;
            if (entryPH != null)
                entryLinkPP = new CustomEntryLinkPropertyProvider(this, name, entryPH) { EntryConstructor = EntryConstructor };
            else entryLinkPP = new CustomEntryLinkPropertyProvider(this, name, entryParsePath) { EntryConstructor = EntryConstructor };
            entryParsePath = entryParsePath ?? entryPH.Behaviors.GetProperty<string>(CofeProperties.FullParseName);

            if (_entryDic.ContainsKey(name))
            {
                int ctr = 1;
                while (_entryDic.ContainsKey(name + ctr.ToString()))
                    ctr++;
                name = name + ctr.ToString();
            }
            _entryDic.Add(name, entryParsePath);
            _entryList.Add(name);
            getEventSource().NotifyCreated(PathFE.Combine(getParseName(), name), false);
            return entryLinkPP;
        }

        public static IManualCofeSystemEventSource dummyEventSource = new DummyCofeSystemEventSource();

        protected IManualCofeSystemEventSource getEventSource()
        {
            string volId = getVolumeId();
            if (volId != null)
            {
                IVolume vol = CofeServices.VolumeRegistrar.LookupVolume(volId);
                if (vol != null && vol.DirectoryLister is EntryListDirectoryLister)
                    return (vol.DirectoryLister as EntryListDirectoryLister).ManualEventSource;
            }
            return dummyEventSource;
        }

        protected string getVolumeId()
        {
            string key, parsePath;
            PathFE.ParseFullParseName(getParseName(), out key, out parsePath);
            return key;
        }

        public void remove(string name)
        {
            lock (_entryDic)
            {
                var pos = getPosition(name);

                if (pos != -1)
                    _entryList.RemoveAt(pos);
                if (name != null)
                    _entryDic.Remove(name);

                getEventSource().NotifyDeleted(PathFE.Combine(getParseName(), name), false);
            }
        }

        protected IEnumerable<IPropertyProvider> listCore(ParameterDic pm)
        {

            pm.IsHandled = true;
            return from key in _entryList
                   select
                   new CustomEntryLinkPropertyProvider(this, key, _entryDic[key]);
            //CofeServices.PathParserManager.Behaviors
            //.TryParsePathAsync(_entryDic[key], ParsableType.ParsePath).Result;

        }

        public int getPosition(string name)
        {
            return _entryList.IndexOf(name);
        }

        public bool setPosition(string name, int position)
        {
            lock (_entryDic)
            {
                var curPosition = getPosition(name);
                if (curPosition != -1)
                {
                    _entryList.RemoveAt(curPosition);
                    _entryList.Insert(Math.Min(position, _entryList.Count - 1), name);
                    getEventSource().NotifyChanged(getParseName(), true);
                    return true;
                }
                return false;
            }
        }

        //public string getName(IPropertyProvider sender)
        //{
        //    string key = find(sender);
        //    if (key == null)
        //        throw new KeyNotFoundException("The link is removed.");
        //    else return key;
        //}

        public void setName(string oldName, string newName)
        {
            lock (_entryDic)
            {
                string key = findParseName(oldName);
                if (key == null)
                    throw new KeyNotFoundException("The link is removed.");

                string parseName = _entryDic[key];    //Reload sender from key.
                _entryDic.Remove(key);      //Then remove it from dic
                _entryDic.Add(newName, parseName); //And re-add it using the newName.     
                _entryList[_entryList.IndexOf(key)] = newName;
            }
        }


        public string findParseName(string name)
        {
            if (_entryDic.ContainsKey(name))
                return _entryDic[name];
            else return null;
        }

        public string findName(string parseName)
        {
            foreach (var k in _entryDic.Keys)
                if (_entryDic[k].Equals(parseName))
                    return k;
            return null;
        }


        #endregion

        #region Data

        /// <summary>
        /// Name then ParseName.
        /// </summary>
        private Dictionary<string, string> _entryDic = new Dictionary<string, string>();
        private List<string> _entryList = new List<string>();
        private IEntryConstructor _entryConstructor = null;

        #endregion

        #region Public Properties

        public IEntryConstructor EntryConstructor
        {
            get { return _entryConstructor ?? CofeServices.ServiceLocater.FindService<IEntryConstructor>(); }
            set { _entryConstructor = value; }
        }

        public Dictionary<string, string> EntryDic { get { return _entryDic; } }

        #endregion
    }

    [Obsolete]
    public class RootEntryListPropertyProvider : CustomEntryListPropertyProvider, IRootEntryListPropertyProvider
    {
        #region Constructor

        internal RootEntryListPropertyProvider(string volName)
            : base(volName)
        {

        }

        #endregion

        #region Methods

        protected override string getParseName()
        {
            string name = _name ?? "Unnamed";
            return "{" + name + "}";
        }

        protected override CofeAttributes getBaseCofeAttributes()
        {
            return CofeAttributes.Browsable;
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion

    }
}
